﻿using System;
using System.Collections.Generic;
using System.Linq;
using ClewareWrapper.Invoke;

namespace ClewareWrapper
{
    /// <summary>
    /// Wraps <see cref="UsbAccessWrapper"/> : automatically call Init and Uninit,
    /// Keep and passes the obj pointer to functions.
    /// </summary>
    public class ClewareManager : IDisposable
    {
        private IntPtr _usbAccessRef;
        private bool _disposed;

        public Dictionary<int, SimpleTrafficLight> SimpleTrafficLights { get; private set; }
        public Dictionary<int, AdvancedTrafficLight> AdvancedTrafficLight { get; private set; }
        public IEnumerable<Device> Devices { get; private set; }

        /// <summary>
        /// Get the version of the DLL.
        /// </summary>
        public int DLLVersion
        {
            get
            {
                return UsbAccessWrapper.GetDLLVersion();
            }
        }

        public ClewareManager()
        {
            _usbAccessRef = UsbAccessWrapper.InitObject();
            Devices = new List<Device>();
            OpenCleware();
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!this._disposed)
            {
                UsbAccessWrapper.UnInitObject(_usbAccessRef);
                _disposed = true;
            }
        }

        /// <summary>
        /// Looks for Cleware USB devices and opens them. <see cref="Devices"/> is
        /// updated.
        /// </summary>
        public void OpenCleware()
        {
            var nbDevices = UsbAccessWrapper.OpenCleware(_usbAccessRef);
            var devices = new List<Device>();
            for (var i = 0; i < nbDevices; i++)
            {
                devices.Add(new Device(this, i));
            }
            Devices = devices;

            SimpleTrafficLights = new Dictionary<int, SimpleTrafficLight>();
            AdvancedTrafficLight = new Dictionary<int, AdvancedTrafficLight>();
            foreach (var device in Devices.Where(d => d.USBType == UsbType.SWITCH1_DEVICE))
            {
                var serialNumber = device.SerialNumber;
                SimpleTrafficLights.Add(serialNumber, new SimpleTrafficLight(device));
                AdvancedTrafficLight.Add(serialNumber, new AdvancedTrafficLight(device));
            }
        }


        /// <summary>
        /// Closes all open Cleware USB devices.
        /// </summary>
        public int CloseCleware()
        {
            return UsbAccessWrapper.CloseCleware(_usbAccessRef);
        }

        /// <summary>
        /// When reading from and writing to the USB device failed several times, the device could
        /// be automatically searched and setup to the initial state.
        /// </summary>
        internal int Recover(int deviceNo)
        {
            return UsbAccessWrapper.Recover(_usbAccessRef, deviceNo);
        }

        /// <summary>
        /// For later use.
        /// </summary>
        internal System.IntPtr GetHandle(int deviceNo)
        {
            return UsbAccessWrapper.GetHandle(_usbAccessRef, deviceNo);
        }

        /// <summary>
        /// For later use. The return value is 0 in case of an error, else > 0.
        /// </summary>
        internal int GetValue(int deviceNo, System.IntPtr buf, int bufsize)
        {
            return UsbAccessWrapper.GetValue(_usbAccessRef, deviceNo, buf, bufsize);
        }

        /// <summary>
        /// For later use. The return value is 0 in case of an error, else > 0..
        /// </summary>
        internal int SetValue(int deviceNo, System.IntPtr buf, int bufsize)
        {
            return UsbAccessWrapper.SetValue(_usbAccessRef, deviceNo, buf, bufsize);
        }

        /// <summary>
        /// For later use. The return value is 0 in case of an error, else > 0.
        /// value: 0=off 7=medium 15=highlight
        /// </summary>
        internal int SetLED(int deviceNo, LedId Led, int value)
        {
            return UsbAccessWrapper.SetLED(_usbAccessRef, deviceNo, Led, value);
        }

        /// <summary>
        /// Turns an USB-Switch on or off. The argument “Switch” defines which switch to set. If
        /// the argument “On” is 1, the switch is turned on. If “On” is 0, the switch is turned off. The
        /// return value is 0 in case of an error, else > 0.
        /// On: 0=off, 1=on
        /// </summary>
        internal int SetSwitch(int deviceNo, SwitchId Switch, int On)
        {
            return UsbAccessWrapper.SetSwitch(_usbAccessRef, deviceNo, Switch, On);
        }

        /// <summary>
        /// Get the current switch state. The argument “Switch” defines which switch to get the status
        /// from. If the returned value is 1 if the switch is on and it is 0 otherwise. In case of an error,
        /// the return value is set to –1.
        /// On: 0=off, 1=on, -1=error
        /// </summary>
        internal int GetSwitch(int deviceNo, SwitchId Switch)
        {
            return UsbAccessWrapper.GetSwitch(_usbAccessRef, deviceNo, Switch);
        }

        /// <summary>
        /// Get the current switch state. The argument “Switch” defines which switch to get the status
        /// from. If the returned value is 1 if the switch is on and it is 0 otherwise. The seqNum
        /// argument should be 0. In case of an error, the return value is set to –1.
        /// This command solves the problem that the USB data is buffered and maybe outdated.
        /// Using GetSeqSwitch will repeatedly get data from the buffer until the state represent the
        /// situation at the time of the call.*
        /// On: 0=off, 1=on, -1=error
        /// </summary>
        internal int GetSeqSwitch(int deviceNo, SwitchId Switch, int seqNum)
        {
            return UsbAccessWrapper.GetSeqSwitch(_usbAccessRef, deviceNo, Switch, seqNum);
        }

        internal int GetSwitchConfig(int deviceNo, ref int switchCount, ref int buttonAvailable)
        {
            return UsbAccessWrapper.GetSwitchConfig(_usbAccessRef, deviceNo, ref switchCount, ref buttonAvailable);
        }

        /// <summary>
        /// Calibration of a temperature device. The second parameter “Sollwert” is the correct
        /// temperature and the “Istwert” is the current value to be corrected. The return value is 0 in
        /// case of an error, else > 0.
        /// </summary>
        internal int SetTempOffset(int deviceNo, double Sollwert, double Istwert)
        {
            return UsbAccessWrapper.SetTempOffset(_usbAccessRef, deviceNo, Sollwert, Istwert);
        }

        /// <summary>
        /// Request for the current temperature. This function will set the temperature and timeID to
        /// the actual values. The returned time is based on a device internal time. This time is used
        /// to assure that two requests deliver different times. At least one second must separate
        /// adjacent calls. If several requests returns the same time, the device may be in trouble and
        /// a reset is necessary. This must be initiated by calling the “ResetDevice” function. The
        /// return value is 0 in case of an error, else > 0.
        /// </summary>
        internal int GetTemperature(int deviceNo, ref double Temperature, ref int timeID)
        {
            return UsbAccessWrapper.GetTemperature(_usbAccessRef, deviceNo, ref Temperature, ref timeID);
        }

        /// <summary>
        /// Request for the current humidity. This function will set the humidity and timeID to the
        /// actual values. The returned time is based on a device internal time. This time is used to
        /// assure that two requests deliver different times. At least two seconds must separate
        /// adjacent calls. The return value is 0 in case of an error, else > 0.
        /// </summary>
        internal int GetHumidity(int deviceNo, ref double Humidity, ref int timeID)
        {
            return UsbAccessWrapper.GetHumidity(_usbAccessRef, deviceNo, ref Humidity, ref timeID);
        }

        /// <summary>
        /// Reset a Cleware device. The return value is 0 in case of an error, else > 0. If the device is
        /// a temperature or humidity sensor, the command causes a hardware reset of the sensor.
        /// Other devices will do a cold start when disconnected for a short time.
        /// </summary>
        internal int ResetDevice(int deviceNo)
        {
            return UsbAccessWrapper.ResetDevice(_usbAccessRef, deviceNo);
        }

        /// <summary>
        /// The USB-Humidity must get a start command to enter the measuring loop. This command
        /// must be send after a reset or when the commands SetValue oder GetValue were used.
        /// When StartDevice is called for other devices it will be ignored. In case of an error, the
        /// return value ist 0, otherwise != 0.
        /// </summary>
        internal int StartDevice(int deviceNo)
        {
            return UsbAccessWrapper.StartDevice(_usbAccessRef, deviceNo);
        }

        /// <summary>
        /// Send a life signal to an USB-Wathdog. The time until the next life signal must be detected
        /// is supplied in minutes. If “time1” is –1 the device is activated immediately (USBAutoReset).
        /// The return value is 0 in case of an error, else > 0.
        /// If the device is an USB-AutoReset “time2” defines the time distance for the secondary
        /// reset for cases the first reset will not restart the PC up to point where life signals will be
        /// send again. The value is defined in minutes and could be in the range 0 – 255. If the value
        /// is 0, the secondary reset is turned off.
        /// If the device is an USB-WatchLight, the green light is turned on when the CalmWatchdog
        /// command is executed. The argument “time1” defines the offset in seconds until the red
        /// light is turned on. The yellow light is turned on after “time2” seconds.
        /// </summary>
        internal int CalmWatchdog(int deviceNo, int minutes, int minutes2restart)
        {
            return UsbAccessWrapper.CalmWatchdog(_usbAccessRef, deviceNo, minutes, minutes2restart);
        }

        /// <summary>
        /// Version number of this device.
        /// </summary>
        internal int GetVersion(int deviceNo)
        {
            return UsbAccessWrapper.GetVersion(_usbAccessRef, deviceNo);
        }

        /// <summary>
        /// Device type. Possible values are defined in the USBtype_enum, eg. SWITCH1_DEVICE
        /// or TEMPERATURE2_DEVICE.
        /// </summary>
        internal UsbType GetUSBType(int deviceNo)
        {
            return UsbAccessWrapper.GetUSBType(_usbAccessRef, deviceNo);
        }

        /// <summary>
        /// Serial number of this device.
        /// </summary>
        internal int GetSerialNumber(int deviceNo)
        {
            return UsbAccessWrapper.GetSerialNumber(_usbAccessRef, deviceNo);
        }

        /// <summary>
        /// If the device is configured as an USB-Watchdog or USB-AutoReset, this counter reflects
        /// how often a reset was done by an USB command.
        /// </summary>
        internal int GetManualOnCount(int deviceNo)
        {
            return UsbAccessWrapper.GetManualOnCount(_usbAccessRef, deviceNo);
        }

        internal int GetManualOnTime(int deviceNo)
        {
            return UsbAccessWrapper.GetManualOnTime(_usbAccessRef, deviceNo);
        }

        /// <summary>
        /// Get the “turned on by USB command” counter (USB-Switch version 15 and higher). In
        /// case of an error –1 will be returned. If the device is configured as an USB-Watchdog or
        /// USB-AutoReset, this counter reflects how often a reset was done by missing the USB life
        /// signal.
        /// </summary>
        internal int GetOnlineOnCount(int deviceNo)
        {
            return UsbAccessWrapper.GetOnlineOnCount(_usbAccessRef, deviceNo);
        }

        /// <summary>
        /// These commands are used with an USB-Switch equiped with a button for manual
        /// switching. In case of an error –1 will be returned.  
        /// </summary>
        internal int GetOnlineOnTime(int deviceNo)
        {
            return UsbAccessWrapper.GetOnlineOnTime(_usbAccessRef, deviceNo);
        }

        /// <summary>
        /// All input channels of the devices USB-Contact and USB-IO16 could be scanned with the
        /// command “GetMultiSwitch”. The argument “mask” indicates the channels which changes
        /// the values since the last call of GetMultiSwitch. Channel 0 is the least significant bit
        /// (LSB). The argument shows the current status of all channels. The argument “seqNum”
        /// should be set to 0.
        /// </summary>
        internal int GetMultiSwitch(int deviceNo, uint param2, ref int mask, uint param4, ref int value, int seqNumber)
        {
            return UsbAccessWrapper.GetMultiSwitch(_usbAccessRef, deviceNo, param2, ref mask, param4, ref value, seqNumber);
        }

        /// <summary>
        /// All output channels of the USB-IO16 will be set with the command “SetMultiSwitch”.
        /// The argument “value” holds the new state of the channels. Channel 0 is the least
        /// significant bit (LSB).
        /// </summary>
        internal int SetMultiSwitch(int deviceNo, uint param2, int value)
        {
            return UsbAccessWrapper.SetMultiSwitch(_usbAccessRef, deviceNo, param2, value);
        }

        /// <summary>
        /// The configuration of the USB-IO16 is defined with the command SetMultiConfig. Every
        /// channel is assigned a bit in the direction argument. Channel 0 is the least significant bit
        /// (LSB). If the bit is 0 the channel is configured as an input, otherwise as an output.
        /// </summary>
        internal int SetMultiConfig(int deviceNo, uint param2, int directions)
        {
            return UsbAccessWrapper.SetMultiConfig(_usbAccessRef, deviceNo, param2, directions);
        }

        /// <summary>
        /// The counter value of the USB-Counteer is requested.
        /// </summary>
        internal int GetCounter(int deviceNo, int counter)
        {
            return UsbAccessWrapper.GetCounter(_usbAccessRef, deviceNo, counter);
        }


        /// <summary>
        /// For internal purposes.
        /// </summary>
        internal int SyncDevice(int deviceNo, uint param2, int mask)
        {
            return UsbAccessWrapper.SyncDevice(_usbAccessRef, deviceNo, param2, mask);
        }

        internal int GetMultiConfig(int deviceNo)
        {
            return UsbAccessWrapper.GetMultiConfig(_usbAccessRef, deviceNo);
        }

        internal int IsAmpel(int deviceNo)
        {
            return UsbAccessWrapper.IsAmpel(_usbAccessRef, deviceNo);
        }

        internal int IsSolidState(int deviceNo)
        {
            return UsbAccessWrapper.IsSolidState(_usbAccessRef, deviceNo);
        }

        internal int IsWatchdogInvert(int deviceNo)
        {
            return UsbAccessWrapper.IsWatchdogInvert(_usbAccessRef, deviceNo);
        }
    }
}
